package org.west.sky.scripture.transaction.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.west.sky.scripture.transaction.SpringUtil;
import org.west.sky.scripture.transaction.model.TableEntity;
import org.west.sky.scripture.transaction.service.ITableService;

/**
 * @author chenghz
 * @date 2022/8/26 14:00
 * 1、REQUIRED spring默认传播行为,支持当前事务，如果当前没有事务，则新建一个事务。
 * 外层、内层方法不管哪个抛出异常，都会回滚，即使在外层方法中捕获内层方法抛出的异常也会回滚
 * 2、REQUIRES_NEW 新建事务，如果当前存在事务，则事务挂起，新增一个事务，新建的事务和当前事务没有任何关系，是两个独立的事务。外层的事务失败回归后，
 * 不能回滚内层事务的运行结果，内层事务失败抛出异常，外层事务有捕获，也能不回滚外层事务
 * 3、SUPPORTS 支持当前事务（和REQUIRED行为一样），如果当前没有事务，就以非事务方式执行
 * 4、NOT_SUPPORTED 不执行当前事务，而总是以非事务的方式执行
 * 5、MANDATORY 支持当前事务（和REQUIRED行为一样），如果当前没有事务，则抛出异常
 * 6、NEVER 以非事务的方式执行，如果当前存在事务，则抛出异常
 * 7、NESTED 如果当前存在事务，则在嵌套事务内执行，如果当前没有事务，则进行与REQUIRED类似的操作
 */
@Service
public class TransactionServiceA {

    @Autowired
    private ITableService tableService;

    @Autowired
    private TransactionServiceB transactionServiceB;

    /**
     * 条件: A、B 均没有事务
     * 结果：A、B均正常插入数据（都未回滚）
     * 原因：没有经过事务控制
     */
    public void methodA_1() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_1();
    }

    /**
     * 条件: A有事务、B没有事务、B报错
     * 结果：A、B均未插入数据（都回滚）
     * 原因：A、B在同一个事务中，可以理解为A、B在一个方法中
     */
    @Transactional
    public void methodA_2_1() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_1();
    }

    /**
     * 条件: A有事务、B没有事务、A报错
     * 结果：A、B均未插入数据（都回滚）
     * 原因：A、B在同一个事务中，可以理解为A、B在一个方法中
     */
    @Transactional
    public void methodA_2_2() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_2();
        throw new RuntimeException();
    }

    /**
     * 条件: A有事务、B没有事务、捕获B抛出的异常
     * 结果：A、B均插入数据（都未回滚）
     * 原因：A、B在同一个事务中，捕获异常后，并没抛出异常，可以理解为A、B在一个方法中，B这段代码加了异常捕获
     */
    @Transactional
    public void methodA_2_3() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        try {
            transactionServiceB.methodB_1();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 条件: A没有事务、B有事务、B抛出异常
     * 结果：A插入数据，B未插入数据（A未回滚，B回滚）
     * 原因：A以无事务方式运行，B以有事务方式运行，A、B独立，互不影响
     */
    public void methodA_3_1() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_3();
    }

    /**
     * 条件: A没有事务、B有事务、捕获B抛出的异常
     * 结果：A插入数据，B未插入数据（A未回滚，B回滚）
     * 原因：A以无事务方式运行，B以有事务方式运行，A、B独立，互不影响
     */
    public void methodA_3_2() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        try {
            transactionServiceB.methodB_3();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 条件: A没有事务、B有事务、A抛出异常
     * 结果：A、B均插入数据（都未回滚）
     * 原因：A以无事务方式运行，B以有事务方式运行，A报错不影响B的事务，A、B独立，互不影响
     */
    public void methodA_3_3() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_4();
        throw new RuntimeException();
    }

    /**
     * 条件: A有事务、B有事务(REQUIRED)、B抛出异常
     * 结果：A、B均未插入数据（都回滚）
     * 原因：B加入A的事务中，A、B在同一个事务中
     */
    @Transactional
    public void methodA_4_1() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_5();
    }

    /**
     * 条件: A有事务、B有事务(REQUIRED)、A抛出异常
     * 结果：A、B均未插入数据（都回滚）
     * 原因：B加入A的事务中，A、B在同一个事务中
     */
    @Transactional
    public void methodA_4_2() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_6();
        throw new RuntimeException();
    }

    /**
     * 条件: A有事务、B有事务(REQUIRED)、捕获B抛出的异常
     * 结果：A、B均未插入数据（都回滚）
     * 原因：B加入A的事务中，A、B在同一个事务中。A、B互相影响，有一个报错就全部回滚，即是在外层方法捕获异常，也会回滚。
     * 区别于{@link #methodA_2_3}
     */
    @Transactional
    public void methodA_4_3() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        try {
            transactionServiceB.methodB_5();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 条件: A有事务、B有事务(REQUIRED_NEW)、B抛出异常
     * 结果：A、B均未插入数据（都回滚）
     * 原因：A事务挂起，B新增一个事务，B报错，B回滚，A未进行捕获，导致A也产生报错，A也回滚
     */
    @Transactional
    public void methodA_5_1() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_7();
    }

    /**
     * 条件: A有事务、B有事务(REQUIRED_NEW)、A抛出异常
     * 结果：A未插入数据，B插入数据（A回滚，B不回滚）
     * 原因：A事务挂起，B新增一个事务，A报错，但是此时B的事务已经提交，B不回滚，A回滚
     */
    @Transactional
    public void methodA_5_2() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_8();
        throw new RuntimeException();
    }

    /**
     * 条件: A有事务、B有事务(REQUIRED_NEW)、捕获B抛出的异常
     * 结果：A插入数据，B未插入数据（A未回滚，B回滚）
     * 原因：A事务挂起，B新增一个事务，B报错，B回滚，但A捕获的B抛出的异常，所以A不回回滚
     * 区别于{@link #methodA_4_3}
     */
    @Transactional
    public void methodA_5_3() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        try {
            transactionServiceB.methodB_7();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 条件: A有事务、B有事务(SUPPORTS)、B抛出异常
     * 结果：A、B均未插入数据（都回滚）
     * 原因：A有事务，B加入A的事务，相当于（REQUIRED）
     * 等于{@link #methodA_4_1}
     */
    @Transactional
    public void methodA_6_1() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_9();
    }

    /**
     * 条件: A有事务、B有事务(SUPPORTS)、A抛出异常
     * 结果：A、B均未插入数据（都回滚）
     * 原因：A有事务，B加入A的事务，相当于（REQUIRED）
     * 等于{@link #methodA_4_2}
     */
    @Transactional
    public void methodA_6_2() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_10();
        throw new RuntimeException();
    }

    /**
     * 条件: A有事务、B有事务(SUPPORTS)、捕获B抛出的异常
     * 结果：A、B均未插入数据（都回滚）
     * 原因：A有事务，B加入A的事务，相当于（REQUIRED）
     * 等于{@link #methodA_4_3}
     */
    @Transactional
    public void methodA_6_3() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        try {
            transactionServiceB.methodB_9();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 条件: A没有事务、B有事务(SUPPORTS)、B抛出异常
     * 结果：A、B均插入数据（都不回滚）
     * 原因：A无事务，B也以非事务方式执行。相当于A、B都没有事务
     * 等于{@link #methodA_1}
     */
    public void methodA_6_4() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_9();
    }

    /**
     * 条件: A有事务、B有事务(NOT_SUPPORTS)、B抛出异常
     * 结果：A未插入数据、B插入数据（A回滚、B不回滚）
     * 原因：A有事务，B总是以非事务的方式执行
     * 区别于{@link #methodA_2_1}
     */
    @Transactional
    public void methodA_7_1() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_11();
    }

    /**
     * 条件: A有事务、B有事务(NOT_SUPPORTS)、A抛出异常
     * 结果：A未插入数据、B插入数据（A回滚、B不回滚）
     * 原因：A有事务，B总是以非事务的方式执行
     * 区别于{@link #methodA_2_2}
     */
    @Transactional
    public void methodA_7_2() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_12();
        throw new RuntimeException();
    }

    /**
     * 条件: A有事务、B有事务(NOT_SUPPORTS)、捕获B抛出的异常
     * 结果：A、B均插入数据（都不回滚）
     * 原因：A有事务，B总是以非事务的方式执行，但是B抛出的异常被捕获，A不会进行回滚操作
     */
    @Transactional
    public void methodA_7_3() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        try {
            transactionServiceB.methodB_11();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 条件: A有事务、B有事务(MANDATORY)、B抛出异常
     * 结果：A、B均未插入数据（都回滚）
     * 原因：A有事务，B支持A的事务，和REQUIRED一样
     * 等于{@link #methodA_4_1}
     */
    @Transactional
    public void methodA_8_1() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_13();
    }

    /**
     * 条件: A有事务、B有事务(MANDATORY)、A抛出异常
     * 结果：A、B均未插入数据（都回滚）
     * 原因：A有事务，B支持A的事务，和REQUIRED一样
     * 等于{@link #methodA_4_2}
     */
    @Transactional
    public void methodA_8_2() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_13();
    }

    /**
     * 条件: A有事务、B有事务(MANDATORY)、捕获B抛出的异常
     * 结果：A、B均未插入数据（都回滚）
     * 原因：A有事务，B支持A的事务，和REQUIRED一样
     * 等于{@link #methodA_4_3}
     */
    @Transactional
    public void methodA_8_3() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        try {
            transactionServiceB.methodB_13();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 条件: A没有事务、B有事务(MANDATORY)
     * 结果：A插入数据、B未插入数据（A未回滚，B未执行插入操作）
     * 原因：MANDATORY如果当前无事务，会报错，因为A先执行且无事物，所以A正常插入。
     * 在进入B方法前就报错 No existing transaction found for transaction marked with propagation 'mandatory'
     * 所以B方法未执行
     */
    public void methodA_8_4() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_14();
    }

    /**
     * 条件: A有事务、B有事务(NEVER)
     * 结果：A、B均未插入数据（A回滚，B未执行插入操作）
     * 原因：A有事务，B判断当前有事务，会报错，所以A会回滚
     * 在进入B方法前就报错 existing transaction found for transaction marked with propagation 'never'
     * 所以B方法未执行
     */
    @Transactional
    public void methodA_9_1() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_16();
    }

    /**
     * 条件: A没有事务、B有事务(NEVER)，B抛出异常
     * 结果：A、B均插入数据（都不回滚）
     * 原因：A无有事务，B以非事务的方式运行，A、B都没有事务，所以都不会回滚
     */
    public void methodA_9_2() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_15();
    }

    /**
     * 条件: A有事务、B有事务(NESTED)，B抛出异常
     * 结果：A、B均未插入数据（都回滚）
     * 原因：A有事务，B嵌套在A事务内，当B抛出异常时，B进行正常回滚操作。由于A并未捕获B的异常，所以A也会回滚
     */
    @Transactional
    public void methodA_10_1() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_17();
    }

    /**
     * 条件: A有事务、B有事务(NESTED)，A抛出异常
     * 结果：A、B均未插入数据（都回滚）
     * 原因：A有事务，B嵌套在A事务内，B事务受A事务的影响，即外层事务可以音响内层事务
     */
    @Transactional
    public void methodA_10_2() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_18();
        throw new RuntimeException();
    }

    /**
     * 条件: A有事务、B有事务(NESTED)，捕获B抛出的异常
     * 结果：A、B均未插入数据（都回滚）
     * 原因：A有事务，B嵌套在A事务内，B抛出的异常被A捕获后，A的事务不受影响
     * 区别于{@link #methodA_4_3}
     */
    @Transactional
    public void methodA_10_3() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        try {
            transactionServiceB.methodB_17();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 条件: A没有事务、B有事务(NESTED)，B抛出异常
     * 结果：A插入数据、B未插入数据（A未回滚、B回滚）
     * 原因：A无事务不回滚，B会像REQUIRED方式一样新建一个事务，当B抛出异常时，B会进行正常回滚。
     */
    public void methodA_10_4() {
        System.out.println("methodA");
        tableService.insertTableA(new TableEntity());
        transactionServiceB.methodB_17();
    }


    /**
     * 条件：通过spring上下文调用本类中加了事务的方法
     * 结果：A、B均未插入数据，都回滚
     * 原因：通过spring容器获取bean,本身就是动态代理的方式，所以事务注解可以生效
     */
    public void methodA() {
        TransactionServiceA bean = SpringUtil.getBean(TransactionServiceA.class);
        bean.methodA_10_4();
    }

    /**
     * 条件：调用本类中加了事务的方法
     * 结果：A、B均正常插入数据，都未回滚
     * 原因：事务本质是动态代理，调用本类方法，不走代理，事务注解不生效
     */
    public void methodA_11_1() {
        methodA_2_1();
    }
}
